﻿//----------------------------------------------------------------------------------------------------------------------
// <copyright file="TaskGridControl.xaml.cs" company="Keith B. Rimington">
//
//   Copyright (c) 2011 Keith B. Rimington
//
//   Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
//     documentation files (the "Software"), to deal in the Software without restriction, including without limitation
//     the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
//     to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in all copies or substantial portions of
//     the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//     THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
//     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//     DEALINGS IN THE SOFTWARE.
//
// </copyright>
//----------------------------------------------------------------------------------------------------------------------

namespace Noc.Pomodoro.Controls
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using Noc.Pomodoro.Core;
    using Noc.Pomodoro.ViewModels;
    using Lookup = Noc.Pomodoro.ViewModels.Lookup;

    /// <summary>
    /// Interaction logic for TaskGridControl.xaml
    /// </summary>
    public partial class TaskGridControl : UserControl
    {
        /// <summary>
        /// Backing store for the TaskUpdated event.
        /// </summary>
        public static readonly RoutedEvent TaskOperationEvent = EventManager.RegisterRoutedEvent("TaskOperation", RoutingStrategy.Bubble, typeof(EventHandler<TaskEventArgs>), typeof(TaskGridControl));

        /// <summary>
        /// Backing store for the Tasks property.
        /// </summary>
        public static readonly DependencyProperty TasksProperty = DependencyProperty.Register("Tasks", typeof(ObservableCollectionEx<TaskViewModel>), typeof(TaskGridControl), new PropertyMetadata(new ObservableCollectionEx<TaskViewModel>(), new PropertyChangedCallback(OnTasksPropertyChanged)));

        /// <summary>
        /// Backing store for the IsInsertEnabled property.
        /// </summary>
        public static readonly DependencyProperty IsTodayProperty = DependencyProperty.Register("IsToday", typeof(bool), typeof(TaskGridControl), new PropertyMetadata(false));

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskGridControl"/> class.
        /// </summary>
        public TaskGridControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Occurs when a new task is inserted in the register.
        /// </summary>
        public event EventHandler<TaskEventArgs> TaskOperation
        {
            add { AddHandler(TaskOperationEvent, value); }
            remove { RemoveHandler(TaskOperationEvent, value); }
        }

        /// <summary>
        /// Gets or sets set of tasks presented in the register.
        /// </summary>
        /// <value>
        /// The set of tasks to present in the register.
        /// </value>
        public ObservableCollectionEx<TaskViewModel> Tasks
        {
            get { return (ObservableCollectionEx<TaskViewModel>)GetValue(TasksProperty); }
            set { SetValue(TasksProperty, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether inserting new tasks is permitted.
        /// </summary>
        /// <value>
        /// A value indicating whether to enable inserts.
        /// </value>
        public bool IsToday
        {
            get { return (bool)GetValue(IsTodayProperty); }
            set { SetValue(IsTodayProperty, value); }
        }

        /// <summary>
        /// Called when the <c>Tasks</c> property changes.
        /// </summary>
        /// <param name="d">The control, as a <c>DependencyObject</c>.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnTasksPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var control = d as TaskGridControl;
            var oldValue = e.OldValue as ObservableCollectionEx<TaskViewModel>;
            var newValue = e.NewValue as ObservableCollectionEx<TaskViewModel>;

            if (oldValue != null)
            {
                oldValue.ItemPropertyChanged -= control.OnTaskItemPropertyChanged;
            }

            if (newValue != null)
            {
                newValue.ItemPropertyChanged += control.OnTaskItemPropertyChanged;
            }
        }

        /// <summary>
        /// Called when a property on an individual task item changes.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnTaskItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "IsRunning")
            {
                var task = sender as TaskViewModel;
                RaiseEvent(new TaskEventArgs(TaskOperationEvent) { Task = task, Operation = TaskOp.Update });
            }
        }

        /// <summary>
        /// Called when the Add Pomodoro icon is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void OnAddPomodoroIconClicked(object sender, MouseButtonEventArgs e)
        {
            var element = sender as FrameworkElement;
            var task = element.DataContext as TaskViewModel;
            task.Pomodoros.Add(new PomodoroViewModel() { IsPlanned = true });

            // Also ensure unplanned+unstarted pomodoros are removed when planning new work.
            foreach (var pomodoro in task.Pomodoros.Where(p => p.IsPlanned == false && p.State == Lookup.PomodoroState.NotStarted))
            {
                pomodoro.IsDeleted = true;
            }

            RaiseEvent(new TaskEventArgs(TaskOperationEvent) { Task = task, Operation = TaskOp.Update });
        }

        /// <summary>
        /// Called when the Delete Pomodoro icon is clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Noc.Pomodoro.Core.PomodoroEventArgs"/> instance containing the event data.</param>
        private void OnPomodoroDelete(object sender, PomodoroEventArgs e)
        {
            var element = (FrameworkElement)sender;
            var task = this.gridToday.SelectedItem as TaskViewModel;
            var pomodoro = element.DataContext as PomodoroViewModel;

            RaiseEvent(new TaskEventArgs(TaskOperationEvent) { Task = task, Operation = TaskOp.Update });
        }

        /// <summary>
        /// Called when the task type icon is clicked, to rotate the task type.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void OnSwitchTaskStateClicked(object sender, MouseButtonEventArgs e)
        {
            var element = (FrameworkElement)sender;
            var task = element.DataContext as TaskViewModel;
            task.TaskType =
                task.TaskType == Lookup.TaskState.Activity ? Lookup.TaskState.UrgentUnplanned :
                task.TaskType == Lookup.TaskState.UrgentUnplanned ? Lookup.TaskState.ToDoToday :
                task.TaskType == Lookup.TaskState.ToDoToday ? Lookup.TaskState.Completed :
                task.TaskType == Lookup.TaskState.Completed ? Lookup.TaskState.Canceled :
                Lookup.TaskState.Activity;
            task.Closed =
                task.TaskType == Lookup.TaskState.Completed ||
                task.TaskType == Lookup.TaskState.Canceled ?
                (DateTime?)DateTime.Now :
                (DateTime?)null;
        }

        /// <summary>
        /// Called when a new item is created on the grid.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Controls.InitializingNewItemEventArgs"/> instance containing the event data.</param>
        private void OnInitializingNewItem(object sender, InitializingNewItemEventArgs e)
        {
            this.Tasks.ItemPropertyChanged -= this.OnTaskItemPropertyChanged;

            var task = e.NewItem as TaskViewModel;
            task.CanRun = !Tasks.Any(t => t.IsRunning);
            RaiseEvent(new TaskEventArgs(TaskOperationEvent) { Task = task, Operation = TaskOp.Insert });

            this.Tasks.ItemPropertyChanged += this.OnTaskItemPropertyChanged;
        }

        /// <summary>
        /// Called when the task toolbar posts an operation.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Noc.Pomodoro.Core.TaskEventArgs"/> instance containing the event data.</param>
        private void OnTaskToolbarOperation(object sender, TaskEventArgs e)
        {
            var task = e.Task;
            PomodoroViewModel pomodoro;
            switch (e.Operation)
            {
                // Attempt to retrieve the pomodoro; if not found, add an unplanned pomodoro to the task.
                case TaskOp.StartPomodoro:
                    pomodoro = task.Pomodoros.FirstOrDefault(p => p.State == Lookup.PomodoroState.NotStarted);
                    if (pomodoro == null)
                    {
                        task.Pomodoros.Add(pomodoro = new PomodoroViewModel() { IsPlanned = false });
                    }

                    pomodoro.State = Lookup.PomodoroState.Running;

                    break;

                // Retrieve the running pomodoro and add an event.
                case TaskOp.InterruptPomodoroInt:
                    pomodoro = task.Pomodoros.FirstOrDefault(p => p.State == Lookup.PomodoroState.Running);
                    pomodoro.Events.Add(new EventViewModel() { EventType = Lookup.PomodoroEventType.InternalInterrupt });
                    break;

                // Retrieve the running pomodoro and add an event.
                case TaskOp.InterruptPomodoroExt:
                    pomodoro = task.Pomodoros.FirstOrDefault(p => p.State == Lookup.PomodoroState.Running);
                    pomodoro.Events.Add(new EventViewModel() { EventType = Lookup.PomodoroEventType.ExternalInterrupt });
                    break;

                // Retrieve the running pomodoro, void it, and add a new pomodoro with the same planning state
                case TaskOp.VoidPomodoro:
                    pomodoro = task.Pomodoros.FirstOrDefault(p => p.State == Lookup.PomodoroState.Running);
                    pomodoro.State = Lookup.PomodoroState.Voided;
                    task.Pomodoros.Add(new PomodoroViewModel() { IsPlanned = pomodoro.IsPlanned });
                    break;
            }

            RaiseEvent(new TaskEventArgs(TaskOperationEvent) { Task = e.Task, Operation = e.Operation });

            task.Pomodoros.RemoveAll(p => p.IsDeleted == true);
        }
    }
}